package com.yzdmw.toutiao.wheeldisk.ui;

import android.graphics.PointF;
import android.graphics.Rect;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;

public class WheeldiskLayoutManager extends RecyclerView.LayoutManager implements RecyclerView.SmoothScroller.ScrollVectorProvider {
    private static final boolean DEBUG = false;
    private static final String TAG = "WheeldiskLayoutManager";
    private final int mItemOffset; // 用来指定滑动多少距离翻过一个页面
    private final int mItemGap; // 用来指定页面的间隔
    private float mOffsetY = 0; // 用来表示累计滑动的偏移量
    private float maxOffsetY = 0; // 用来表示最大的位移量
    private int mFirstVisibleItemPos; // 用来表示第一个可见的页面的坐标
    private float mPercent = 0; // 用来表示item间隔的滑动百分比
    private static final int mItemCountInScreen = 8; // 用来表示整个页面中的子页面数

    @Override
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
        return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    }

    WheeldiskLayoutManager(){
        this.mItemOffset = 600;
        this.mItemGap = 10;
    }

    /**
     * onLayoutChildren 可以计算每个 Item 的位置，并对 Item 布局，可以实现我们的轮盘定位
     * @param recycler 回收器
     * @param state 传递的状态
     */
    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        //如果没有item，直接直接退出
        if (getItemCount() == 0 || state.isPreLayout())
            return;
        //暂时分离全部有效的Item
        detachAndScrapAttachedViews(recycler);
        //开始布局
        onlayoutItems(recycler, calculateRects(state.getItemCount()));
    }
    private int getVerticalSpace() {
        return getHeight() - getPaddingTop() - getPaddingBottom();
    }
    private int getHorizontalSpace() {
        return getWidth() - getPaddingLeft() - getPaddingRight();
    }
    /**
     * 根据位置计算边框
     * @param index 当前相对位置
     */
    public Rect getRect(int index) {
        float fraction = mPercent;
        int height = getVerticalSpace()/6;
        int width = getHorizontalSpace()/3;
        if (index >= 5) { // 在左边的缓冲队列中
            return new Rect(0,(int)(height*fraction)-height*(index-4),width,(int)(height*fraction)-height*(index-5));
        } else if (index <= -2) { // 在右边的缓冲队列中
            return new Rect(width, height*(-2-index)-(int) (height * fraction),width*3,height*(-1-index)-(int)(height*fraction));
        }
        switch (index){
            case 4: return new Rect(0,(int)(height*fraction),width,height+(int)(height*2*fraction));
            case 3: return new Rect(0,height+(int)(height*2f*fraction),width,height*3+(int)(height*2f*fraction));
            case 2: return new Rect(0,height*3+(int)(height*2f*fraction),width,height*5+(int)(height*1f*fraction));
            case 1: return new Rect((int)(width*fraction),height*5,width+(int)(width*2*fraction),height*6);
            case 0: return new Rect(width,height*5-(int)(height*4f*fraction),width*3,height*6-(int)(height*fraction));
            case -1: return new Rect(width,height-(int)(height*fraction),width*3,height*5-(int)(height*4*fraction));
            default: return null;
        }
    }
    /**
     * 计算item滑动间距的百分比，顺带更新滚动边界 和 第一个可见的下标
     * @param itemCount
     * @return item滑动间距的百分比
     */
    private void calculatePercent(int itemCount) {
        maxOffsetY = (itemCount-3) * mItemOffset;
        float currentDistance = -Float.MAX_VALUE;
        for (int i = 0; i < itemCount; i++) {
            currentDistance = i * mItemOffset - mOffsetY;
            // 判断当前距离 >= 0的即表示可见
            if (currentDistance >= 0) {
                // 得到第一个可见的position
                mFirstVisibleItemPos = i;
                break;
            }
        }

        //得到当前距离百分比
        mPercent = 1-currentDistance / mItemOffset;
        if(DEBUG)
            Log.d(TAG,"calculatePercent -> percent:"+mPercent);
    }
    /**
     * 计算每一项的轮廓
     * @param itemCount adapter中传入的数据量
     * @return 每一项的轮廓的数组
     */
    private List<Rect> calculateRects(int itemCount) {
        //得到当前距离百分比
        calculatePercent(itemCount);
        //结束的position
        int endIndex = mFirstVisibleItemPos + mItemCountInScreen;
        //防止溢出
        if (endIndex > getItemCount()) {
            endIndex = getItemCount();
        }

        List<Rect> result = new ArrayList<>();
        for (int i = mFirstVisibleItemPos; i < endIndex; i++) {
            //根据百分比从Keyframes中取出对应的坐标和角度
            Rect rect = getRect(i-mFirstVisibleItemPos-2);
            if (rect == null) {
                continue;
            }
            if(DEBUG)
			    Log.d(TAG,"calculatePoints -> x:"+rect.left+",y:"+rect.top);
            //添加进list中
            result.add(rect);
        }
        return result;
    }

    /**
     * 根据传入的边框对目标进行渲染
     * @param recycler 回收器
     * @param rects 边框列表
     */
    private void onlayoutItems(RecyclerView.Recycler recycler, List<Rect> rects) {
        View item;
        for (int i = 0; i < rects.size() ; i++) {
            //根据position获取View
            item = recycler.getViewForPosition(mFirstVisibleItemPos + i);
            //添加进去，当然里面不一定每次都是调用RecyclerView的addView方法的，
            //如果是从缓存区里面找到的，只需调用attachView方法把它重新连接上就 行了。
            addView(item);

            // 根据间隔适当修改
            int l = rects.get(i).left+mItemGap;
            int t = rects.get(i).top+mItemGap;
            int r = rects.get(i).right-mItemGap;
            int b = rects.get(i).bottom-mItemGap;

            //测量item
            measureChild(item, getHorizontalSpace()-r+l, getVerticalSpace()-b+t);

            //进行布局
            layoutDecorated(item, l, t, r, b);
        }
    }

    /**
     * 启用垂直滚动
     */
    @Override
    public boolean canScrollVertically() {
        return true;
    }

    @Override
    public boolean canScrollHorizontally() {
        return false;
    }

    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        //分离和临时回收
        detachAndScrapAttachedViews(recycler);
        //更新偏移量
        dy = (int)updateOffsetY(dy);
        //开始布局
        onlayoutItems(recycler, calculateRects(state.getItemCount()));
        //回收较远的item
        recycleChildren(recycler);
        return dy;
    }

    /**
     * 更新记录的偏移量
     * @param dy 单次偏移量
     * @return 将确保边界的偏移量返回
     */
    private int updateOffsetY(int dy){
        //更新offset
        mOffsetY += dy;
        //item总长度相对于路径总长度多出来的部分
        if (mOffsetY < 0) { //到顶
            dy -= mOffsetY;
            mOffsetY = 0;
        } else if (mOffsetY > maxOffsetY){ //到底
            dy -= mOffsetY - maxOffsetY;
            mOffsetY = maxOffsetY;
        }
        return dy;
    }

    /**
     * 回收离屏视图
     * @param recycler 回收器
     */
    private void recycleChildren(RecyclerView.Recycler recycler) {
        //拿到临时缓存
        List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList();
        //遍历，然后先移除，后回收，其实也就是removeAndRecycleView方法所做的事
        for (int i = 0; i < scrapList.size(); i++) {
            RecyclerView.ViewHolder holder = scrapList.get(i);
            removeView(holder.itemView);
            recycler.recycleView(holder.itemView);
        }
    }

    /**
     * 在更换适配器的时候，重置相关属性
     * @param oldAdapter 旧适配器
     * @param newAdapter 新适配器
     */
    @Override
    public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) {
        removeAllViews();
        mOffsetY = 0;
        requestLayout();
    }

    /**
     * 用来计算方向向量
     * @param targetPosition 目标在adapter中的索引
     * @return 需要进行的偏移向量
     */
    @Override
    public PointF computeScrollVectorForPosition(int targetPosition) {
        if (getChildCount() == 0) {
            return null;
        }
        final int firstChildPos = getPosition(getChildAt(0));
        final int direction = targetPosition < firstChildPos ? -1 : 1;
        return new PointF(0, direction);
    }

    @Override
    public void startSmoothScroll(RecyclerView.SmoothScroller smoothScroller) {
        super.startSmoothScroll(smoothScroller);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                       int position) {
        LinearSmoothScroller linearSmoothScroller =
                new LinearSmoothScroller(recyclerView.getContext());
        linearSmoothScroller.setTargetPosition(position);
        startSmoothScroll(linearSmoothScroller);
    }
}
